#pragma once
#include "Log.hpp"
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>
#include <sstream>
#include "bundle.h"

namespace my_cloud{
    namespace fs = std::experimental::filesystem;
    class FileUtil{
    private:
        std::string _filename;
    public:
        FileUtil(const std::string& filename): _filename(filename){}
        size_t FileSize(){
            struct stat st;
            if(stat(_filename.c_str(), &st) == -1){
                LOG(WARING, "FileSize Error!");
                return 0;
            }
            return st.st_size;
        }
        time_t FileMTime(){
            struct stat st;
            if(stat(_filename.c_str(), &st) == -1){
                LOG(WARING, "FileMTime Error!");
                return 0;
            }
            return st.st_mtime;
        }
        time_t FileATime(){
            struct stat st;
            if(stat(_filename.c_str(), &st) == -1){
                LOG(WARING, "FileATime Error!");
                return 0;
            }
            return st.st_atime;
        }
        std::string FileName(){
            int pos = _filename.rfind("/");
            if(pos == std::string::npos){
                return _filename;
            }
            return _filename.substr(pos + 1);
        }
        bool GetPosLen(std::string& content, size_t pos, size_t len){
            if(pos + len > FileSize()){
                LOG(WARING, "GetPosLen Error!");
                return false;
            }
            std::ifstream ifs(_filename, std::ios::binary);
            if(ifs.is_open() == false){
                LOG(WARING, "Open " + _filename + " Error!");
                return false;
            }
            content.resize(len);
            ifs.seekg(pos, std::ios::beg);
            ifs.read(&content[0], len);
            if(ifs.good() == false){
                LOG(WARING, "GetPosLen Error!");
                return false;
            }
            ifs.close();
            return true;
        }
        bool GetContent(std::string& content){
            if(GetPosLen(content, 0, FileSize()) == false){
                LOG(WARING, "GetContent Error!");
                return false;
            }
            return true;
        }
        bool SetContent(const std::string& content){
            //std::ofstream ofs(_filename, std::ios::binary | std::ios::trunc);
            std::ofstream ofs(_filename, std::ios::binary);
            
            if(ofs.is_open() == false){
                LOG(WARING, "Open : " + _filename + " Error!");
                return false;
            }
            ofs.write(content.c_str(), content.size());
            if(ofs.good() == false){
                LOG(WARING, "SetContent ERROR!");
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const std::string& filename){
            std::string content;
            if(GetContent(content) == false){
                LOG(WARING, "Compress GetContent Error!");
                return false;
            }
            std::string paked = bundle::pack(bundle::LZIP, content);
            FileUtil fu(filename);
            if(fu.SetContent(paked) == false){
                LOG(WARING, "Compress SetContent ERROR!");
                return false;
            }   
            return true;
        }
        bool UnCompress(const std::string& filename){
            std::string content;
            if(GetContent(content) == false){
                LOG(WARING, "UnCompress GetContent Error!");
                return false;
            }
            std::string unpacked = bundle::unpack(content);
            FileUtil fu(filename);
            if(fu.SetContent(unpacked) == false){
                LOG(WARING, "UnCompress SetContent ERROR!");
                return false;
            }   
            return true;
        }
        bool Exists(){
            return fs::exists(_filename);
        }
        bool Remove(){
            if(Exists() == false){
                return true;
            }
            remove(_filename.c_str());
            return true;
        }
        bool CreateDir(){
            if(Exists() == true){
                return true;
            }
            return fs::create_directories(_filename);
        }
        bool ScanDir(std::vector<std::string>& arry){
            for(auto& p : fs::directory_iterator(_filename)){
                if(fs::is_directory(p) == true){
                    continue;
                }
                arry.push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    };

    class JsonUtil{
    public:
        static bool Serialization(const Json::Value& root, std::string* str){
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            if(sw->write(root, &ss) != 0){
                LOG(WARING, "Serialization Error!");
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialization(Json::Value* root, const std::string &str){
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            if(cr->parse(str.c_str(), str.c_str() + str.size(), root, &err) == false){
                LOG(WARING, "UnSerialization Error!");
                return false;
            }
            return true;
        }
    };
}
